# Interface via which we receive event notifications from other guest
# OSes. This interface also allows us to clear/acknowledge outstanding
- # notifications --- successive notifications for the same channel are
- # dropped until the first notification is cleared.
+ # notifications.
notifier = xend.utils.notifier()
##
break
(idx, type) = notification
+ if not control_list.has_key(idx):
+ continue
+
+ (port, rbuf, wbuf, con_if) = control_list[idx]
+ work_done = False
+
# If we pick up a disconnect notification then we do any necessary
- # cleanup, even if the event channel doesn't belong to us.
- # This is intended to prevent the event-channel port space from
- # getting clogged with stale connections.
- if type == notifier.DISCONNECT:
+ # cleanup.
+ if type == notifier.EXCEPTION:
ret = xc.evtchn_status(idx)
- if ret['status'] == 'interdomain':
- notifier.clear(idx, notifier.NORMAL)
- notifier.clear(idx, notifier.DISCONNECT)
- if control_list.has_key(idx):
- (port, rbuf, wbuf, con_if) = control_list[idx]
- con_if.close()
- del control_list[idx], port, rbuf, wbuf, con_if
- elif ret['status'] == 'unbound':
- # There's noone to do the closure for us...
- xc.evtchn_close(idx)
-
- # A standard notification: probably means there are messages to
- # read or that there is space to write messages.
- elif type == notifier.NORMAL and control_list.has_key(idx):
- (port, rbuf, wbuf, con_if) = control_list[idx]
- work_done = False
-
- # We clear the notification before doing any work, to avoid
- # races.
- notifier.clear(idx, notifier.NORMAL)
-
- # Read incoming requests. Currently assume that request
- # message always containb console data.
- while port.request_to_read():
- msg = port.read_request()
- rbuf.write(msg.get_payload())
- port.write_response(msg)
- work_done = True
-
- # Incoming responses are currently thrown on the floor.
- while port.response_to_read():
- msg = port.read_response()
- work_done = True
-
- # Send as much pending console data as there is room for.
- while not wbuf.empty() and port.space_to_write_request():
- msg = xend.utils.message(0, 0, 0)
- msg.append_payload(wbuf.read(msg.MAX_PAYLOAD))
- port.write_request(msg)
- work_done = True
-
- # Finally, notify the remote end of any work that we did.
- if work_done:
- port.notify()
+ if ret['status'] == 'unbound':
+ notifier.unbind(idx)
+ con_if.close()
+ del control_list[idx], port, rbuf, wbuf, con_if
+ continue
+
+ # Read incoming requests. Currently assume that request
+ # message always containb console data.
+ while port.request_to_read():
+ msg = port.read_request()
+ rbuf.write(msg.get_payload())
+ port.write_response(msg)
+ work_done = True
+
+ # Incoming responses are currently thrown on the floor.
+ while port.response_to_read():
+ msg = port.read_response()
+ work_done = True
+
+ # Send as much pending console data as there is room for.
+ while not wbuf.empty() and port.space_to_write_request():
+ msg = xend.utils.message(0, 0, 0)
+ msg.append_payload(wbuf.read(msg.MAX_PAYLOAD))
+ port.write_request(msg)
+ work_done = True
+
+ # Finally, notify the remote end of any work that we did.
+ if work_done:
+ port.notify()
+
+ # Unmask notifications for this port.
+ notifier.unmask(idx)
## automatically allocated.
##
def new_control_interface(dom, console_port=-1):
- # Allocate an event channel. Clear pending notifications.
+ # Allocate an event channel and binbd to it.
port = xend.utils.port(dom)
- xend.main.notifier.clear(port.local_port, xend.main.notifier.NORMAL)
- xend.main.notifier.clear(port.local_port, xend.main.notifier.DISCONNECT)
+ xend.main.notifier.bind(port.local_port)
# If necessary, compute a suitable TCP port for console I/O.
if console_port < 0:
#define EVTCHN_DEV_MAJOR 10
#define EVTCHN_DEV_MINOR 200
#define PORT_NORMAL 0x0000 /* A standard event notification. */
-#define PORT_DISCONNECT 0x8000 /* A port-disconnect notification. */
+#define PORT_EXCEPTION 0x8000 /* An exceptional notification. */
#define PORTIDX_MASK 0x7fff /* Strip subtype to obtain port index. */
-#define EVTCHN_RESET _IO('E', 1) /* Clear notification buffer. Clear errors. */
+/* /dev/xen/evtchn ioctls: */
+/* EVTCHN_RESET: Clear and reinit the event buffer. Clear error condition. */
+#define EVTCHN_RESET _IO('E', 1)
+/* EVTCHN_BIND: Bind to teh specified event-channel port. */
+#define EVTCHN_BIND _IO('E', 2)
+/* EVTCHN_UNBIND: Unbind from the specified event-channel port. */
+#define EVTCHN_UNBIND _IO('E', 3)
/* Size of a machine page frame. */
#define PAGE_SIZE 4096
return Py_None;
}
-static PyObject *xu_notifier_clear(PyObject *self, PyObject *args)
+static PyObject *xu_notifier_unmask(PyObject *self, PyObject *args)
{
xu_notifier_object *xun = (xu_notifier_object *)self;
u16 v;
- int idx, type;
+ int idx;
- if ( !PyArg_ParseTuple(args, "ii", &idx, &type) )
+ if ( !PyArg_ParseTuple(args, "i", &idx) )
return NULL;
-
- v = (u16)idx | (u16)type;
+ v = (u16)idx;
+
(void)write(xun->evtchn_fd, &v, sizeof(v));
Py_INCREF(Py_None);
return Py_None;
}
+static PyObject *xu_notifier_bind(PyObject *self, PyObject *args)
+{
+ xu_notifier_object *xun = (xu_notifier_object *)self;
+ int idx;
+
+ if ( !PyArg_ParseTuple(args, "i", &idx) )
+ return NULL;
+
+ if ( ioctl(xun->evtchn_fd, EVTCHN_BIND, idx) != 0 )
+ return PyErr_SetFromErrno(PyExc_IOError);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyObject *xu_notifier_unbind(PyObject *self, PyObject *args)
+{
+ xu_notifier_object *xun = (xu_notifier_object *)self;
+ int idx;
+
+ if ( !PyArg_ParseTuple(args, "i", &idx) )
+ return NULL;
+
+ if ( ioctl(xun->evtchn_fd, EVTCHN_UNBIND, idx) != 0 )
+ return PyErr_SetFromErrno(PyExc_IOError);
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
static PyObject *xu_notifier_fileno(PyObject *self, PyObject *args)
{
xu_notifier_object *xun = (xu_notifier_object *)self;
METH_VARARGS,
"Read a (@port, @type) pair.\n" },
- { "clear",
- (PyCFunction)xu_notifier_clear,
+ { "unmask",
+ (PyCFunction)xu_notifier_unmask,
+ METH_VARARGS,
+ "Unmask notifications for a @port.\n" },
+
+ { "bind",
+ (PyCFunction)xu_notifier_bind,
+ METH_VARARGS,
+ "Get notifications for a @port.\n" },
+
+ { "unbind",
+ (PyCFunction)xu_notifier_unbind,
METH_VARARGS,
- "Clear a (@port, @type) pair.\n" },
+ "No longer get notifications for a @port.\n" },
{ "fileno",
(PyCFunction)xu_notifier_fileno,
static PyObject *xu_notifier_getattr(PyObject *obj, char *name)
{
- if ( strcmp(name, "DISCONNECT") == 0 )
- return PyInt_FromLong(PORT_DISCONNECT);
+ if ( strcmp(name, "EXCEPTION") == 0 )
+ return PyInt_FromLong(PORT_EXCEPTION);
if ( strcmp(name, "NORMAL") == 0 )
return PyInt_FromLong(PORT_NORMAL);
return Py_FindMethod(xu_notifier_methods, obj, name);
/*
* Port 0 is the fallback port for VIRQs that haven't been explicitly
- * bound yet. The exception is the 'error VIRQ', which is permanently
+ * bound yet. The exception is the 'misdirect VIRQ', which is permanently
* bound to port 0.
*/
if ( ((port = p->virq_to_evtchn[virq]) != 0) ||
- (virq == VIRQ_ERROR) ||
+ (virq == VIRQ_MISDIRECT) ||
((port = get_free_port(p)) < 0) )
goto out;
chn1 = p1->event_channel;
- /* NB. Port 0 is special (VIRQ_ERROR). Never let it be closed. */
+ /* NB. Port 0 is special (VIRQ_MISDIRECT). Never let it be closed. */
if ( (port1 <= 0) || (port1 >= p1->max_event_channel) )
{
rc = -EINVAL;
p->max_event_channel = INIT_EVENT_CHANNELS;
memset(p->event_channel, 0, INIT_EVENT_CHANNELS * sizeof(event_channel_t));
p->event_channel[0].state = ECS_VIRQ;
- p->event_channel[0].u.virq = VIRQ_ERROR;
+ p->event_channel[0].u.virq = VIRQ_MISDIRECT;
return 0;
}
* Virtual interrupts that a guest OS may receive from the hypervisor.
*/
-#define VIRQ_BLKDEV 0 /* A block device response has been queued. */
-#define VIRQ_TIMER 1 /* A timeout has been updated. */
-#define VIRQ_DIE 2 /* OS is about to be killed. Clean up please! */
-#define VIRQ_DEBUG 3 /* Request guest to dump debug info (gross!) */
-#define VIRQ_NET 4 /* There are packets for transmission. */
-#define VIRQ_PS2 5 /* PS/2 keyboard or mouse event(s) */
-#define VIRQ_STOP 6 /* Prepare for stopping and possible pickling */
-#define VIRQ_EVTCHN 7 /* Event pending on an event channel */
-#define VIRQ_VBD_UPD 8 /* Event to signal VBDs should be reprobed */
-#define VIRQ_CONSOLE 9 /* This is only for domain-0 initial console. */
-#define VIRQ_PHYSIRQ 10 /* Event to signal pending physical IRQs. */
-#define VIRQ_ERROR 11 /* Catch-all virtual interrupt. */
-#define NR_VIRQS 12
+#define VIRQ_BLKDEV 0 /* A block device response has been queued. */
+#define VIRQ_TIMER 1 /* A timeout has been updated. */
+#define VIRQ_DIE 2 /* OS is about to be killed. Clean up please! */
+#define VIRQ_DEBUG 3 /* Request guest to dump debug info (gross!) */
+#define VIRQ_NET 4 /* There are packets for transmission. */
+#define VIRQ_PS2 5 /* PS/2 keyboard or mouse event(s) */
+#define VIRQ_STOP 6 /* Prepare for stopping and possible pickling */
+#define VIRQ_EVTCHN 7 /* Event pending on an event channel */
+#define VIRQ_VBD_UPD 8 /* Event to signal VBDs should be reprobed */
+#define VIRQ_CONSOLE 9 /* This is only for domain-0 initial console. */
+#define VIRQ_PHYSIRQ 10 /* Event to signal pending physical IRQs. */
+#define VIRQ_MISDIRECT 11 /* Catch-all virtual interrupt. */
+#define NR_VIRQS 12
/*
* MMU_XXX: specified in least 2 bits of 'ptr' field. These bits are masked
* 2. EXCEPTION -- notifies the domain that there has been some
* exceptional event associated with this channel (e.g. remote
* disconnect, physical IRQ error). This bit is cleared by the guest.
+ * A 0->1 transition of this bit will cause the PENDING bit to be set.
* 3. MASK -- if this bit is clear then a 0->1 transition of PENDING
- * or EXCEPTION will cause an asynchronous upcall to be scheduled.
- * This bit is only updated by the guest. It is read-only within Xen.
- * If a channel becomes pending or an exceptional event occurs while
- * the channel is masked then the 'edge' is lost (i.e., when the
- * channel is unmasked, the guest must manually handle pending
- * notifications as no upcall will be scheduled by Xen).
+ * will cause an asynchronous upcall to be scheduled. This bit is only
+ * updated by the guest. It is read-only within Xen. If a channel
+ * becomes pending while the channel is masked then the 'edge' is lost
+ * (i.e., when the channel is unmasked, the guest must manually handle
+ * pending notifications as no upcall will be scheduled by Xen).
*
- * To expedite scanning of pending notifications and exceptions, any
- * 0->1 transition on an unmasked channel causes a corresponding bit in
- * a 32-bit selector to be set. Each bit in the selector covers a 32-bit
- * word in the PENDING or EXCEPTION bitfield array.
+ * To expedite scanning of pending notifications, any 0->1 pending
+ * transition on an unmasked channel causes a corresponding bit in a
+ * 32-bit selector to be set. Each bit in the selector covers a 32-bit
+ * word in the PENDING bitfield array.
*/
u32 evtchn_pending[32];
u32 evtchn_pending_sel;
u32 evtchn_exception[32];
- u32 evtchn_exception_sel;
u32 evtchn_mask[32];
/*
static inline void evtchn_set_exception(struct task_struct *p, int port)
{
- shared_info_t *s = p->shared_info;
- if ( !test_and_set_bit(port, &s->evtchn_exception[0]) &&
- !test_bit (port, &s->evtchn_mask[0]) &&
- !test_and_set_bit(port>>5, &s->evtchn_exception_sel) )
- guest_notify(p);
+ if ( !test_and_set_bit(port, &p->shared_info->evtchn_exception[0]) )
+ evtchn_set_pending(p, port);
}
/*
}
register_console(&kcons_info);
-
- evtchn_clear_error_virq();
}
static DECLARE_WAIT_QUEUE_HEAD(evtchn_wait);
static struct fasync_struct *evtchn_async_queue;
-/*
- * Pending normal notifications and pending exceptional notifications.
- * 'Pending' means that we received an upcall but this is not yet ack'ed
- * from userspace by writing to /dev/xen/evtchn.
- */
-static u32 pend_nrm[32], pend_exc[32];
+/* Which ports is user-space bound to? */
+static u32 bound_ports[32];
static spinlock_t lock;
-void evtchn_device_upcall(int port, int exception)
+void evtchn_device_upcall(int port)
{
u16 port_subtype;
+ shared_info_t *s = HYPERVISOR_shared_info;
spin_lock(&lock);
mask_evtchn(port);
+ clear_evtchn(port);
- if ( likely(!exception) )
- {
- clear_evtchn(port);
- set_bit(port, &pend_nrm[0]);
+ if ( likely(!synch_test_and_clear_bit(port, &s->evtchn_exception[0])) )
port_subtype = PORT_NORMAL;
- }
else
- {
- clear_evtchn_exception(port);
- set_bit(port, &pend_exc[0]);
port_subtype = PORT_EXCEPTION;
- }
if ( ring != NULL )
{
static void __evtchn_reset_buffer_ring(void)
{
- u32 m;
- unsigned int i, j;
-
- /* Initialise the ring with currently outstanding notifications. */
+ /* Initialise the ring to empty. Clear errors. */
ring_cons = ring_prod = ring_overflow = 0;
-
- for ( i = 0; i < 32; i++ )
- {
- m = pend_exc[i];
- while ( (j = ffs(m)) != 0 )
- {
- m &= ~(1 << --j);
- ring[ring_prod++] = (u16)(((i * 32) + j) | PORT_EXCEPTION);
- }
-
- m = pend_nrm[i];
- while ( (j = ffs(m)) != 0 )
- {
- m &= ~(1 << --j);
- ring[ring_prod++] = (u16)(((i * 32) + j) | PORT_NORMAL);
- }
- }
}
static ssize_t evtchn_read(struct file *file, char *buf,
spin_lock_irq(&lock);
for ( i = 0; i < (count/2); i++ )
- {
- clear_bit(kbuf[i]&PORTIDX_MASK,
- (kbuf[i]&PORT_EXCEPTION) ? &pend_exc[0] : &pend_nrm[0]);
- unmask_evtchn(kbuf[i]&PORTIDX_MASK);
- }
+ if ( test_bit(kbuf[i], &bound_ports[0]) )
+ unmask_evtchn(kbuf[i]);
spin_unlock_irq(&lock);
rc = count;
static int evtchn_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
- if ( cmd != EVTCHN_RESET )
- return -EINVAL;
-
+ int rc = 0;
+
spin_lock_irq(&lock);
- __evtchn_reset_buffer_ring();
+
+ switch ( cmd )
+ {
+ case EVTCHN_RESET:
+ __evtchn_reset_buffer_ring();
+ break;
+ case EVTCHN_BIND:
+ if ( !test_and_set_bit(arg, &bound_ports[0]) )
+ unmask_evtchn(arg);
+ else
+ rc = -EINVAL;
+ break;
+ case EVTCHN_UNBIND:
+ if ( test_and_clear_bit(arg, &bound_ports[0]) )
+ mask_evtchn(arg);
+ else
+ rc = -EINVAL;
+ break;
+ default:
+ rc = -ENOSYS;
+ break;
+ }
+
spin_unlock_irq(&lock);
- return 0;
+ return rc;
}
static unsigned int evtchn_poll(struct file *file, poll_table *wait)
static int evtchn_release(struct inode *inode, struct file *filp)
{
+ int i;
+
spin_lock_irq(&lock);
if ( ring != NULL )
{
free_page((unsigned long)ring);
ring = NULL;
}
+ for ( i = 0; i < NR_EVENT_CHANNELS; i++ )
+ if ( test_and_clear_bit(i, &bound_ports[0]) )
+ mask_evtchn(i);
spin_unlock_irq(&lock);
evtchn_dev_inuse = 0;
/* Upcall to generic IRQ layer. */
extern asmlinkage unsigned int do_IRQ(int irq, struct pt_regs *regs);
-static void evtchn_handle_normal(shared_info_t *s, struct pt_regs *regs)
-{
- unsigned long l1, l2;
- unsigned int l1i, l2i, port;
- int irq;
-
- l1 = xchg(&s->evtchn_pending_sel, 0);
- while ( (l1i = ffs(l1)) != 0 )
- {
- l1i--;
- l1 &= ~(1 << l1i);
-
- l2 = s->evtchn_pending[l1i] & ~s->evtchn_mask[l1i];
- while ( (l2i = ffs(l2)) != 0 )
- {
- l2i--;
- l2 &= ~(1 << l2i);
-
- port = (l1i << 5) + l2i;
- if ( (irq = evtchn_to_irq[port]) != -1 )
- do_IRQ(irq, regs);
- else
- evtchn_device_upcall(port, 0);
- }
- }
-}
-
-static void evtchn_handle_exceptions(shared_info_t *s, struct pt_regs *regs)
-{
- unsigned long l1, l2;
- unsigned int l1i, l2i, port;
- int irq;
-
- l1 = xchg(&s->evtchn_exception_sel, 0);
- while ( (l1i = ffs(l1)) != 0 )
- {
- l1i--;
- l1 &= ~(1 << l1i);
-
- l2 = s->evtchn_exception[l1i] & ~s->evtchn_mask[l1i];
- while ( (l2i = ffs(l2)) != 0 )
- {
- l2i--;
- l2 &= ~(1 << l2i);
-
- port = (l1i << 5) + l2i;
- if ( (irq = evtchn_to_irq[port]) != -1 )
- {
- printk(KERN_ALERT "Error on IRQ line %d!\n", irq);
- synch_clear_bit(port, &s->evtchn_exception[0]);
- }
- else
- evtchn_device_upcall(port, 1);
- }
- }
-}
-
void evtchn_do_upcall(struct pt_regs *regs)
{
- unsigned long flags;
+ unsigned long l1, l2;
+ unsigned int l1i, l2i, port;
+ int irq;
+ unsigned long flags;
shared_info_t *s = HYPERVISOR_shared_info;
local_irq_save(flags);
while ( synch_test_and_clear_bit(0, &s->evtchn_upcall_pending) )
{
- if ( s->evtchn_pending_sel != 0 )
- evtchn_handle_normal(s, regs);
- if ( s->evtchn_exception_sel != 0 )
- evtchn_handle_exceptions(s, regs);
+ l1 = xchg(&s->evtchn_pending_sel, 0);
+ while ( (l1i = ffs(l1)) != 0 )
+ {
+ l1i--;
+ l1 &= ~(1 << l1i);
+
+ l2 = s->evtchn_pending[l1i] & ~s->evtchn_mask[l1i];
+ while ( (l2i = ffs(l2)) != 0 )
+ {
+ l2i--;
+ l2 &= ~(1 << l2i);
+
+ port = (l1i << 5) + l2i;
+ if ( (irq = evtchn_to_irq[port]) != -1 )
+ do_IRQ(irq, regs);
+ else
+ evtchn_device_upcall(port);
+ }
+ }
}
local_irq_restore(flags);
NULL
};
-static void error_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static void misdirect_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
- printk(KERN_ALERT "unexpected VIRQ_ERROR trap to vector %d\n", irq);
+ /* nothing */
}
-static struct irqaction error_action = {
- error_interrupt,
+static struct irqaction misdirect_action = {
+ misdirect_interrupt,
SA_INTERRUPT,
0,
- "error",
+ "misdirect",
NULL,
NULL
};
/* No event-channel -> IRQ mappings. */
for ( i = 0; i < NR_EVENT_CHANNELS; i++ )
+ {
evtchn_to_irq[i] = -1;
+ mask_evtchn(i); /* No event channels are 'live' right now. */
+ }
/* No IRQ -> event-channel mappings. */
for ( i = 0; i < NR_IRQS; i++ )
irq_desc[pirq_to_irq(i)].handler = &pirq_type;
}
- (void)setup_irq(bind_virq_to_irq(VIRQ_ERROR), &error_action);
+ (void)setup_irq(bind_virq_to_irq(VIRQ_MISDIRECT), &misdirect_action);
}
void evtchn_do_upcall(struct pt_regs *regs);
/* Entry point for notifications into the userland character device. */
-void evtchn_device_upcall(int port, int exception);
+void evtchn_device_upcall(int port);
static inline void mask_evtchn(int port)
{
static inline void unmask_evtchn(int port)
{
shared_info_t *s = HYPERVISOR_shared_info;
- int need_upcall = 0;
synch_clear_bit(port, &s->evtchn_mask[0]);
* The following is basically the equivalent of 'hw_resend_irq'. Just like
* a real IO-APIC we 'lose the interrupt edge' if the channel is masked.
*/
-
- /* Asserted a standard notification? */
if ( synch_test_bit (port, &s->evtchn_pending[0]) &&
- !synch_test_and_set_bit(port>>5, &s->evtchn_pending_sel) )
- need_upcall = 1;
-
- /* Asserted an exceptional notification? */
- if ( synch_test_bit (port, &s->evtchn_exception[0]) &&
- !synch_test_and_set_bit(port>>5, &s->evtchn_exception_sel) )
- need_upcall = 1;
-
- /* If asserted either type of notification, check the master flags. */
- if ( need_upcall &&
+ !synch_test_and_set_bit(port>>5, &s->evtchn_pending_sel) &&
!synch_test_and_set_bit(0, &s->evtchn_upcall_pending) &&
!synch_test_bit (0, &s->evtchn_upcall_mask) )
evtchn_do_upcall(NULL);
synch_clear_bit(port, &s->evtchn_exception[0]);
}
-static inline void evtchn_clear_error_virq(void)
-{
- /*
- * XXX This prevents a bogus 'VIRQ_ERROR' when interrupts are enabled
- * for the first time. This works because by this point all important
- * VIRQs (eg. timer) have been properly bound.
- */
- synch_clear_bit(0, &HYPERVISOR_shared_info->evtchn_pending[0]);
-}
-
/*
* CHARACTER-DEVICE DEFINITIONS
*/
/* /dev/xen/evtchn ioctls: */
/* EVTCHN_RESET: Clear and reinit the event buffer. Clear error condition. */
-#define EVTCHN_RESET _IO('E', 1)
+#define EVTCHN_RESET _IO('E', 1)
+/* EVTCHN_BIND: Bind to teh specified event-channel port. */
+#define EVTCHN_BIND _IO('E', 2)
+/* EVTCHN_UNBIND: Unbind from the specified event-channel port. */
+#define EVTCHN_UNBIND _IO('E', 3)
#endif /* __ASM_EVTCHN_H__ */